package com.biz.primus.ms.product.service;

import com.biz.primus.base.constant.Constant;
import com.biz.primus.base.enums.vendor.VendorType;
import com.biz.primus.base.exception.utils.AssertUtils;
import com.biz.primus.base.vo.*;
import com.biz.primus.base.vo.commodity.ProductPriceVo;
import com.biz.primus.base.vo.commodity.ProductStockVo;
import com.biz.primus.commodity.exception.CommodityExceptionType;
import com.biz.primus.commodity.vo.CommodityVo;
import com.biz.primus.commodity.vo.frontend.ProductCustomModelVo;
import com.biz.primus.common.utils.DistanceCalc;
import com.biz.primus.common.utils.StringTool;
import com.biz.primus.model.price.vo.PriceReqVo;
import com.biz.primus.model.promotion.vo.req.*;
import com.biz.primus.ms.base.service.AbstractBaseService;
import com.biz.primus.ms.product.feign.*;
import com.biz.primus.ms.product.interfaces.ProductPriceService;
import com.biz.primus.ms.product.interfaces.ProductPrototype;
import com.biz.primus.ms.product.interfaces.ProductStockService;
import com.biz.primus.ms.product.interfaces.impl.TypeAProductPrototype;
import com.biz.primus.ms.product.interfaces.impl.TypeBProductPrototype;
import com.biz.primus.ms.product.interfaces.vo.CommodityCatchVo;
import com.biz.primus.search.vo.search.ProductSearchResultVo;
import com.biz.primus.search.vo.search.SearchProductRequestVo;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author jiangnan
 * @Description: 商品抽象接口
 * @create 2017-10-12-下午3:49
 */
@Slf4j
public abstract class AbstractProductService extends AbstractBaseService {

    @Autowired
    private DepotFeignClient depotService;

    @Autowired
    private CommodityFeignClient commodityFeignClient;

    @Autowired
    private PriceFeignClient priceFeignClient;

    @Autowired
    private ProductPriceService productPriceService;

    @Autowired
    private ProductStockService productStockService;

    @Autowired
    private PromotionFeignClient promotionFeignClient;

    @Autowired
    private ProductSearchFeignClient productSearchFeignClient;


    protected ProductSearchResultVo<ProductSearchResultEntityVo> doSearch(SearchProductRequestVo reqVo){
        return productSearchFeignClient.searchProduct(reqVo);
    }


    private LoadingCache<VendorType, CommodityCatchVo> commodityCatchVo =
            CacheBuilder.newBuilder().initialCapacity(20).expireAfterWrite(20, TimeUnit.SECONDS)
                    .build(new CacheLoader<VendorType, CommodityCatchVo>() {
                        @Override
                        public CommodityCatchVo load(VendorType key) throws Exception {
                            return commodityFeignClient.getCommodityCatch(key.getValue());
                        }
                    });


    protected List<ProductPrototype> buildTypeAProductPrototypes(List<CommodityVo> ros, List<Integer> stocks, List<PriceVo> priceVos, ProductValidator validator, List<String> saleArea) {
        if (log.isDebugEnabled()) {
            log.debug("buildTypeAProductPrototypes ros size: {}, stocks size: {}, priceVos size: {}",
                    ros == null ? 0 : ros.size(), stocks == null ? 0 : stocks.size(), priceVos == null ? 0 : priceVos.size());
        }
        List<ProductPrototype> prototypes = Lists.newArrayList();
        if (CollectionUtils.isEmpty(ros) || CollectionUtils.isEmpty(stocks)
                || CollectionUtils.isEmpty(priceVos)
                || (ros.size() != stocks.size() && ros.size() != priceVos.size())) {
            if (log.isDebugEnabled()) {
                log.debug("There're no products");
            }
        } else {
            int i = 0;
            while (i < ros.size()) {
                CommodityVo commodityVo = ros.get(i);
                Integer stock = stocks.get(i);
                PriceVo priceVo = priceVos.get(i);
                prototypes.add(this.buildTypeAProductPrototype(commodityVo, stock, priceVo, validator, StringTool.split(saleArea.get(i), ",")));
                i++;
            }
        }
        if (log.isDebugEnabled()) {
            log.debug("buildTypeAProductPrototypes prototypes: {}", prototypes);
        }
        return prototypes;
    }

    /**
     * 构造A类商品数据
     *
     * @param ro      商品Ro
     * @param stock   商品库存信息（A类商品）
     * @param priceVo 价格Ro
     * @return A类商品数据
     */
    protected ProductPrototype buildTypeAProductPrototype(CommodityVo ro, Integer stock, PriceVo priceVo, ProductValidator validator, List<String> saleArea) {
        if (log.isDebugEnabled()) {
            log.debug("buildTypeAProductPrototype ro: {}, stock: {}, priceVo: {}", ro, stock, priceVo);
        }
        if (ro == null) {
            return null;
        }

        return new TypeAProductPrototype();
    }


    protected TypeProductsGroupVo buildTypeProductsGroup(List<String> productCodes) {
        if (log.isDebugEnabled()) {
            log.debug("buildProductPrototypes productCodes: {}", productCodes);
        }
        TypeProductsGroupVo vo = new TypeProductsGroupVo();
        if (CollectionUtils.isEmpty(productCodes)) {
            return vo;
        }
        List<String> allTypeAProductCodes = null;
        List<String> allTypeBProductCodes = null;
        try {
            allTypeAProductCodes = commodityCatchVo.get(VendorType.TYPE_A).getProductCodes();
            allTypeBProductCodes = commodityCatchVo.get(VendorType.TYPE_B).getProductCodes();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        List<ProductTypeWithIndexVo> indexVos = Lists.newArrayList();

        int i = 0;
        for (String productCode : productCodes) {
            VendorType vendorType = null;
            if (allTypeAProductCodes.contains(productCode)) {
                vendorType = VendorType.TYPE_A;
            }
            if (allTypeBProductCodes.contains(productCode)) {
                vendorType = VendorType.TYPE_B;
            }

            if (vendorType != null) {
                indexVos.add(new ProductTypeWithIndexVo(i++, vendorType));
            }
        }

        Collection<String> typeAProductCodes = CollectionUtils.retainAll(productCodes, allTypeAProductCodes);
        Collection<String> typeBProductCodes = CollectionUtils.retainAll(productCodes, allTypeBProductCodes);
        vo.setIndexVos(indexVos);
        vo.setTypeAProductCodes(Lists.newArrayList(typeAProductCodes));
        vo.setTypeBProductCodes(Lists.newArrayList(typeBProductCodes));
        return vo;
    }

    public MultiPrototypeBuilder multiBuilder(List<String> productCodes) {
        return new MultiPrototypeBuilder(productCodes);
    }

    public class MultiPrototypeBuilder {

        private List<ProductPriceVo> productPriceVos;

        private Boolean priceValid = Boolean.FALSE;

        private List<ProductStockVo> productStockVos;

        private Boolean stockValid = Boolean.FALSE;
        //commodity 元素不会为空
        private List<CommodityVo> commodityVos;

        private List<ProductSpecialOfferPromotionRespVo> productSpecialOfferPromotions;

        private Boolean specialOfferPromotionsValid = Boolean.FALSE;

        private List<ProductPromotionTagRespVo> productPromotionTag;

        private Boolean promotionTagValid = Boolean.FALSE;

        private List<ProductPrototype> productPrototypes;

        private List<String> validProductCodes = Lists.newArrayList();

        private Integer distance;

        public MultiPrototypeBuilder(List<String> productCodes) {
            commodityVos = commodityFeignClient.findCommoditiesByCodes(productCodes).stream().filter(Objects::nonNull).collect(Collectors.toList());
            this.validProductCodes = commodityVos.stream().map(CommodityVo::getProductCode).collect(Collectors.toList());
            if (productCodes.size()!=validProductCodes.size()){
                log.info("productCodes.size()!=validProductCodes.size()");
            }
        }

        public MultiPrototypeBuilder buildPrices(String depotCode) {
            PriceReqVo priceReqVo = new PriceReqVo(validProductCodes, depotCode, Constant.VIRTUAL_DEPOT_CODE);
            productPriceVos = productPriceService.getPrices(priceReqVo);
            priceValid = Boolean.TRUE;
            return this;
        }

        /**
         * 构建带简单特价的价格
         */
        public MultiPrototypeBuilder buildPrices(String depotCode, IPromotionReqVo iPromotionReqVo) {
            PriceReqVo priceReqVo = new PriceReqVo(validProductCodes, depotCode, Constant.VIRTUAL_DEPOT_CODE);
            productPriceVos = productPriceService.getPrices(priceReqVo);
            priceValid = Boolean.TRUE;
            MultiProductSpecialOfferPriceReqVo reqVo = new MultiProductSpecialOfferPriceReqVo();
            reqVo.setAppChannel(iPromotionReqVo.getAppChannel());
            reqVo.setCityId(iPromotionReqVo.getCityId());
            reqVo.setMemberId(iPromotionReqVo.getMemberId());
            reqVo.setProductIds(commodityVos.stream().map(CommodityVo::getProductId).collect(Collectors.toList()));
            reqVo.setProvinceId(iPromotionReqVo.getProvinceId());
            reqVo.setShopId(iPromotionReqVo.getShopId());
            reqVo.setZoneId(iPromotionReqVo.getZoneId());
            productSpecialOfferPromotions = promotionFeignClient.getProductSpecialOfferPromotion(reqVo);
            if (commodityVos.size() != productSpecialOfferPromotions.size()){
                log.warn("构造商品原型数据失败,商品:"+commodityVos.size()+" 简单特价:"+productStockVos.size());
                productSpecialOfferPromotions=null;
            }
            specialOfferPromotionsValid = Boolean.TRUE;
            return this;
        }

        public MultiPrototypeBuilder buildLimitation(BigDecimal userLat, BigDecimal userLon, BigDecimal depotLat, BigDecimal depotLon) {
            Double calc;
            if (userLat != null && userLon != null && depotLat != null && depotLon != null) {
                calc = DistanceCalc.distance(userLat.doubleValue(), userLon.doubleValue(), depotLat.doubleValue(), depotLon.doubleValue());
                this.distance = calc.intValue();
            }
            return this;
        }

        public MultiPrototypeBuilder buildStocks(String depotCode) {
            productStockVos = productStockService.getStocks(commodityVos, depotCode);
            AssertUtils.isTrue(commodityVos.size() == productStockVos.size(),
                    CommodityExceptionType.COMMON_EXCEPTION, "构造商品原型数据失败,商品:"+commodityVos.size()+" 库存:"+productStockVos.size());
            stockValid = Boolean.TRUE;
            return this;
        }

        public MultiPrototypeBuilder buildPromotionTag() {
            ProductPromotionTagReqVo reqVo = new ProductPromotionTagReqVo();
            List<ProductPromotionTagReqItemVo> items = Lists.newArrayList();
            for (CommodityVo commodityVo : commodityVos) {
                items.add(new ProductPromotionTagReqItemVo(commodityVo.getProductId(), commodityVo.getCategoryId(), commodityVo.getBrandId()));
            }
            reqVo.setItems(items);
            productPromotionTag = promotionFeignClient.getProductPromotionTags(reqVo);
            promotionTagValid = Boolean.TRUE;
            return this;
        }


        public List<ProductPrototype> build() {
            productPrototypes = Lists.newArrayList();
            for (int i = 0; i < commodityVos.size(); i++) {
                if (commodityVos.get(i).getProductType() == VendorType.TYPE_B.getValue()) {
                    productPrototypes.add(new TypeBProductPrototype(commodityVos.get(i), priceValid?productPriceVos.get(i):null,
                            stockValid?productStockVos.get(i):null, specialOfferPromotionsValid?productSpecialOfferPromotions.get(i):null,
                            promotionTagValid?productPromotionTag.get(i):null,distance));
                } else {
                    log.error("不被支持的商品类型 productCode {}", commodityVos.get(i).getProductCode());
                    productPrototypes.add(null);
                }
            }
            return productPrototypes;
        }

    }


    public Builder builder(String productCode) {
        return new Builder(productCode);
    }

    public class Builder {

        private Integer distance;

        private ProductPriceVo productPriceVo;

        private ProductStockVo productStockVo;

        private CommodityVo commodityVo;

        private ProductSpecialOfferPromotionRespVo productSpecialOfferPromotion;

        private ProductCustomModelVo productCustomModelVo;

        /**
         * 商品详情促销信息
         */
        List<PromotionBasicInfoVo> promotionBasicInfoVos;

        ProductPrototype productPrototype;

        public Builder(String productCode) {
            commodityVo = commodityFeignClient.findCommodityByCode(productCode);
            AssertUtils.notNull(commodityVo, CommodityExceptionType.PRODUCT_NOT_FOUND);
        }

        public Builder buildPrice(String depotCode, IPromotionReqVo iPromotionReqVo) {
            productPriceVo = productPriceService.getPrices(new PriceReqVo(new ArrayList<String>() {{
                add(commodityVo.getProductCode());
            }},
                    depotCode, Constant.VIRTUAL_DEPOT_CODE)).get(0);
            SingleProductSpecialOfferPriceReqVo reqVo = new SingleProductSpecialOfferPriceReqVo();
            reqVo.setProductId(commodityVo.getProductId());
            reqVo.setAppChannel(iPromotionReqVo.getAppChannel());
            reqVo.setCityId(iPromotionReqVo.getCityId());
            reqVo.setMemberId(iPromotionReqVo.getMemberId());
            reqVo.setProvinceId(iPromotionReqVo.getProvinceId());
            reqVo.setShopId(iPromotionReqVo.getShopId());
            reqVo.setZoneId(iPromotionReqVo.getZoneId());
            productSpecialOfferPromotion = promotionFeignClient.getProductSpecialOfferPromotion(reqVo);
            return this;
        }

        public Builder builStock(String depotCode) {
            productStockVo = productStockService.getStocks(new ArrayList<CommodityVo>() {{
                add(commodityVo);
            }}, depotCode).get(0);
            return this;
        }

        public Builder buildPromotionBasicInfo(IPromotionReqVo promotionReqVo) {
            ProductPromotionReqVo reqVo = new ProductPromotionReqVo();
            ProductPromotionTagReqItemVo itemVo = new ProductPromotionTagReqItemVo();
            itemVo.setBrandId(this.commodityVo.getBrandId());
            itemVo.setCategoryId(this.commodityVo.getCategoryId());
            itemVo.setProductId(this.commodityVo.getProductId());
            reqVo.setProductItem(itemVo);
            reqVo.setAppChannel(promotionReqVo.getAppChannel());
            reqVo.setCityId(promotionReqVo.getCityId());
            reqVo.setMemberId(promotionReqVo.getMemberId());
            reqVo.setProvinceId(promotionReqVo.getProvinceId());
            reqVo.setShopId(promotionReqVo.getShopId());
//            reqVo.setUserFirstOrder();
            reqVo.setZoneId(promotionReqVo.getZoneId());
            promotionBasicInfoVos = promotionFeignClient.getProductPromotions(reqVo);
            return this;
        }

        public Builder buildCustomModel() {
            ProductCustomModelVo respVo = commodityFeignClient.getProductCustomModelVo(commodityVo.getProductCode()
                    , String.valueOf(commodityVo.getCategoryId()));
            this.productCustomModelVo = respVo;
            return this;
        }

        public Builder buildLimitation(BigDecimal latitude, BigDecimal longitude, BigDecimal depotLat, BigDecimal depotLon) {
            Double calc;
            if (latitude != null && longitude != null && depotLat != null && depotLon != null) {
                calc = DistanceCalc.distance(latitude.doubleValue(), longitude.doubleValue(), depotLat.doubleValue(), depotLon.doubleValue());
                this.distance = calc.intValue();
            }
            return this;
        }

        public ProductPrototype build() {
            if (Objects.equals(commodityVo.getProductType(), VendorType.TYPE_B.getValue())) {
                productPrototype = new TypeBProductPrototype(commodityVo, productPriceVo, productStockVo, productSpecialOfferPromotion,
                        promotionBasicInfoVos, null,distance);
            }
            return productPrototype;
        }

    }

}
